package org.ecpkn.PAGridSim.FUPAOptimization;

import java.util.ArrayList;
import java.util.Collections;

import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
import org.apache.commons.math3.analysis.solvers.NewtonRaphsonSolver;
import org.ecpkn.PAGridSim.core.offlineOptimization.OfflineOptimizationAlgo;
import org.ecpkn.PAGridSim.core.offlineOptimization.OptimizedResultEntry;

/**
 * @author Lin Zhou
 * 
 *         Module:optimization
 * 
 */
public class FUPAOptimization extends OfflineOptimizationAlgo {

	public static final class Const {
		public static final double PARAM_A = 0.15;
		public static final double PARAM_B = 1.4;
		public static final double PARAM_C = 15.2;
		public static final double PARAM_D = 0;
		// The static power D is 50.8, we don't take it into consideration in
		// the algo
	}

	public FUPAOptimization(Double workload) {
		super(workload);
	}

	@Override
	public OptimizedResultEntry optimize() {

		OptimizedResultEntry optRE = null;

		// Power consumption model adapted from <Energy-Efficiency Model and
		// Measuring
		// Approach for Cloud Computing> by SONG Jie, LI Tian-Tian, YAN
		// Zhen-Xing,
		// NA Jun, ZHU Zhi-Liang+
		// (Software College, Northeastern University, Shenyang 110819,
		// China)
		// P=A*f^3+B*u*f^3+C*u+D
		// Assume that u*f=K,constant,so P=A*f^3+B*K*f^2+C*K/f+D
		// A,B,C,D power consumption parameters which needed to be mesured
		// for
		// each node
		// D represente the power consumption without workload
		// A,B,C are related to the utilisation and the frequency
		// In [0,+infinish),the funcion steadily decrease then steadily
		// increase,so
		// it has a minimum value
		// Ktotal=K*NumberOfNodes

		// Assume that the maximum utilisation=1,and the maximum
		// frequency=4(HZ),0<u<1,0<f<4,so
		// the maximum single cpu workload 0<K<4,the minimum number of nodes
		// nMin=
		int nMin = (int) Math.floor(workload_ / 4) + 1;
		// Math.floor(workload_/4) can be 0,but nMin should be at least 1
		// Normally,the optimised point is around u=1,frequency=1.7,so the
		// minimum
		// single cpu workload should be > 1.7, it's enough to take 0.5
		int nMax = (int) Math.floor(workload_ / 0.5) + 1;
		int j = 0;
		ArrayList<Double> powerMinList = new ArrayList<Double>();
		ArrayList<Double> fMinList = new ArrayList<Double>();
		ArrayList<Double> uMinList = new ArrayList<Double>();
		double[] fonctionParams = new double[5];
		double constant;
		double premier = 0;
		double square = 0;
		double cube;
		double quad = 3 * Const.PARAM_A;
		fonctionParams[1] = premier;
		fonctionParams[2] = square;
		fonctionParams[4] = quad;
		PolynomialFunction pf;
		NewtonRaphsonSolver newtonSolver = new NewtonRaphsonSolver(
				(double) 0.001);
		double uMin = 1;
		double fMin = 1;
		double workloadM = 1;
		for (int n = nMin; n < nMax + 1; n++) {
			j = n - nMin;
			workloadM = workload_ / n;
			// the derivation of P=A*f^3+B*K*f^2+C*K/f+D is
			// (3A*f^4+2*B*K*f^3-C*K)/(f^2)
			// the root of this derivation is the optimized point for the
			// function
			constant = -Const.PARAM_C * workloadM;
			cube = 2 * Const.PARAM_B * workloadM;
			fonctionParams[0] = constant;
			fonctionParams[3] = cube;
			pf = new PolynomialFunction(fonctionParams);
			// max 20 iteration,min interval 0.5,max 1.8
			fMin = newtonSolver.solve(30, pf, 0.5, 1.8);
			uMin = workloadM / fMin;
			if (uMin > 1) {
				uMin = 1;
				fMin = workloadM / uMin;
			}
			fMinList.add(j, fMin);
			uMinList.add(j, uMin);
			powerMinList.add(j, (Const.PARAM_A * fMin * fMin * fMin
					+ Const.PARAM_B * workloadM * fMin * fMin + workloadM
					* Const.PARAM_C / fMin + Const.PARAM_D)
					* n);
		}

		double optimizedP = (double) Collections.min(powerMinList);

		for (int n = nMin; n < nMax + 1; n++) {
			j = n - nMin;
			if (powerMinList.get(j).equals(optimizedP)) {
				optRE = new OptimizedResultEntry(uMinList.get(j),
						fMinList.get(j) * 1000, n);
//				 without optimization algo
//				 optRE = new OptimizedResultEntry(1.0,
//				 2.8 * 1000, 5);
			}
		}
		return optRE;
	}
}
